#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#include "Log.hpp"
#include "ThreadPool.hpp"
#include "task.hpp"
#include "Demon.hpp"

const int defaultsockfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10;
extern Log lg;
enum
{
    Use_error = 1,
    Socket_error,
    Bind_error,
    Listen_error
};
class tcpserver;

class ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, uint16_t port, tcpserver *t) : sockfd(fd), clientip(ip), clientport(port), tcsv(t)
    {
    }

public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    tcpserver *tcsv;
};
class tcpserver
{
public:
    tcpserver(const uint16_t &port, const std::string &ip = defaultip) : listensock_(defaultsockfd), port_(port), ip_(ip)
    {
    }
    void Initserver()
    {
        signal(SIGPIPE, SIG_IGN);
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            lg(Fatal, "create socket err,errno:%d,error string:%s", errno, strerror(errno));
            exit(Socket_error);
        }
        lg(Info, "create socket success,errno:%d,error string:%s", errno, strerror(errno));
        int opt = 1;
        setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        struct sockaddr_in local;
        memset(&local, 9, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &local.sin_addr);
        if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind socket err,errno:%d,error string:%s", errno, strerror(errno));
            exit(Bind_error);
        }
        lg(Info, "bind socket success,errno:%d,error string:%s", errno, strerror(errno));
        if (listen(listensock_, backlog) < 0)
        {
            lg(Fatal, "listen socket err,errno:%d,error string:%s", errno, strerror(errno));
            exit(Listen_error);
        }
        lg(Info, "listen socket success,errno:%d,error string:%s", errno, strerror(errno));
    }
    // void service(int sockfd, const uint16_t &clientport, const std::string &clientip)
    // {
    //     char buff[4096];
    //     while (true)
    //     {
    //         ssize_t n = read(sockfd, buff, sizeof(buff));

    //         if (n > 0)
    //         {
    //             buff[n] = 0;
    //             std::cout << "client say#" << buff << std::endl;
    //             std::string echo_string = "server say#";
    //             echo_string += buff;
    //             write(sockfd, echo_string.c_str(), echo_string.size());
    //         }
    //         else if (n == 0)
    //         {
    //             lg(Info, "%s:%d quit,server close sockfd:%d", clientip.c_str(), clientport, sockfd);
    //             break;
    //         }
    //         else
    //         {
    //             lg(Warning, "read error,server close sockfd:%d", sockfd);
    //             break;
    //         }
    //     }
    // }
    // static void *Coutine(void *args)
    // {
    //     pthread_detach(pthread_self());
    //     ThreadData *td = static_cast<ThreadData *>(args);
    //     td->tcsv->service(td->sockfd, td->clientport, td->clientip);
    //     return nullptr;
    // }
    void Start()
    {
        Demon();
        threadpool<task>::GetInstance()->Start();
        // signal(SIGCHLD, SIG_IGN);//信号忽略
        while (true)
        {
            lg(Info, "server running...");
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensock_, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                lg(Warning, "accept socket err,errno:%d,error string:%s", errno, strerror(errno));
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
            lg(Info, "get a new link...,sockfd:%d,client ip:%s,client port:%d ", sockfd, clientip, clientport);
            // version 1
            //  service(sockfd, clientport, clientip);
            //  close(sockfd);
            // version 2
            // pid_t id = fork();
            // if (id == 0)
            // {
            //     // child
            //     close(listensock_);
            //     if (fork() > 0)
            //         exit(0);
            //     service(sockfd, clientport, clientip); // 孙子进程
            //     close(sockfd);
            //     exit(0);
            // }
            // // father
            // close(sockfd);
            // pid_t rid = waitpid(id, nullptr, 0);
            // version 3
            // pthread_t tid;
            // ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
            // pthread_create(&tid, nullptr, Coutine, td);
            // version 4 线程池
            task t(sockfd, clientport, clientip);
            threadpool<task>::GetInstance()->Push(t);
        }
    }
    ~tcpserver() {}

private:
    int listensock_;
    uint16_t port_;
    std::string ip_;
};